package by.dvrudenko.avtobaza.services.impl;

import java.sql.Timestamp;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.persistence.metamodel.SingularAttribute;

import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import by.dvrudenko.avtobaza.dataaccess.CarDao;
import by.dvrudenko.avtobaza.dataaccess.DriverDao;
import by.dvrudenko.avtobaza.dataaccess.RouteDao;
import by.dvrudenko.avtobaza.dataaccess.UserOrderDao;
import by.dvrudenko.avtobaza.dataaccess.en.Restriction;
import by.dvrudenko.avtobaza.datamodel.Car;
import by.dvrudenko.avtobaza.datamodel.Car_;
import by.dvrudenko.avtobaza.datamodel.Route;
import by.dvrudenko.avtobaza.datamodel.Route_;
import by.dvrudenko.avtobaza.services.CarService;

/*
 @Service - (Сервис-слой приложения, бизнес логика) Аннотация обьявляющая, что этот класс
 представляет собой сервис – компонент сервис-слоя. Сервис является подтипом класса @Component.
 Использование данной аннотации позволит искать бины-сервисы автоматически.
 */

@Service
public class CarServiceImpl implements CarService {
	private static final Logger LOGGER = LoggerFactory
			.getLogger(CarServiceImpl.class);

	@Inject
	private CarDao daoCar;
	
	@Inject
	private RouteDao daoRoute;
	
	@Inject
	private UserOrderDao daoUserOrderDao;
	
	@Inject
	private DriverDao daoDriver;

	@PostConstruct
	private void init() {
		// Код который будет выполнен после созданиея бина
		// this method will be called by Spring after bean instantiation. Can be
		// used for any initialization process.
		LOGGER.info("Instance of CarService is created. Class is: {}",
				getClass().getName());
	}

	@Override
	public Car get(Long id) {
		Car entity = daoCar.getById(id);
		return entity;
	}

	@Override
	public void saveOrUpdate(Car car) {
		if (car.getId() == null) {
			LOGGER.debug("Save new: {car}", car);
			daoCar.insert(car);
		} else {
			LOGGER.debug("Update: {car}", car);
			daoCar.update(car);
		}
	}

	@Override // удалить машину
	public void delete(Car car) {
		/*
		 * перед удалением машины, необходимо удалить все
		 * маршруты по к-ым она ездила 
		 * в routeList содержатся все маршруты по id машины
		 */
		
		List<Route> routeList = daoRoute.getRouteByAtr(Route_.car ,car.getId());		
		for (int i=0;i<routeList.size();i++){
			LOGGER.debug("Remove: {route}", routeList.get(i).getId());
			daoRoute.delete(routeList.get(i).getId());
		}
		LOGGER.debug("Remove: {car}", car);
		daoCar.delete(car.getId());
		
		
	}

	@Override
	public void deleteAll() {
		LOGGER.debug("Remove all cars");
		daoCar.deleteAll();
	}

	@Override
	public List<Car> getAllCars(SingularAttribute<Car, ?> attr, boolean ascending, int startRecord, int pageSize) {
		return daoCar.getAllCars(attr, ascending, startRecord, pageSize);
	}

	@Override
	public List<Car> getAllCars() {
        return daoCar.getAllCars();
	}
	
	

	@Override // все машины на задании
	public List<Car> getAllCarInWork(Boolean work) {
		return daoCar.getAllByFieldRestriction(Car_.work, work);
	}

	@Override // все машины в рабочем состоянии
	public List<Car> getAllCarInCondition(Boolean condition) {
		return daoCar.getAllByFieldRestriction(Car_.carCondition, condition);
	}
	
	@Override
	public List<Car> getAllCarByYear(Timestamp year , Restriction restriction) {
		return daoCar.getAllByFieldByDate(Car_.year, year, restriction);
	}

	@Override
	public List<Car> getAllCarByCarryingCapacity(Integer carryingCapacity, Restriction restriction) {
		return daoCar.getAllByFieldGreaterOrLessThan(Car_.carryingCapacity, carryingCapacity, restriction);
	}

	@Override
	public List<Car> getAllCarByVolumeCapacity(Integer volumeCapacity, Restriction restriction) {
		return daoCar.getAllByFieldGreaterOrLessThan(Car_.volumeCapacity, volumeCapacity, restriction);
	}

	@Override // все машины у которых  Capacity выше заданного значения Integer capacity
	public List<Car> getAllCarsInConditionByCapacity(Integer capacity) {
		return daoCar.getAllCarsInConditionByField(Car_.carryingCapacity, capacity);
	}

	@Override // все машины в раб состоянии у которых  Volume выше заданного значения Integer volume
	public List<Car> getAllCarsInConditionByVolume(Integer volume) {
		return daoCar.getAllCarsInConditionByField(Car_.volumeCapacity, volume);
	}

	@Override // все маш в раб состоянии и к-х  capacity volume больше переданного значения
	public List<Car> getAllFreeCarsByCapacityAndVolume(Integer capacity, Integer volume) {
		return daoCar.getAllCarsByWorkConditionVolumeCapacity(volume, capacity, false, true);
	}

	@Override // изменить значение Boolean атрибута
	public void changeBooleanValue(SingularAttribute<Car, Boolean> attr,Long id, Boolean bool) {
		Validate.notNull(bool, "Boolean value cannot be Null!");
		Car car = daoCar.getById(id);
		
		if (attr.equals(Car_.work)) {
			car.setWork(bool);
		} else if (attr.equals(Car_.carCondition)){
			car.setCarCondition(bool);
		} else {
			throw new IllegalArgumentException (attr + " - SingularAttribute  Car_ "
					+ "must be equlas work or carCondition");	
		}	
		saveOrUpdate(car);	
	}

	@Override
	public Car getRadomCar() {
		return daoCar.randomEntity();
	}

	@Override
	public Long getCount() {
		return daoCar.getCount();
	}

	@Override
	public List<Car> getAllCarsInConditionByWork(Boolean work) {
		return daoCar.getAllCarsInConditionByWork(work);
	}
	
	
}
